package com.zorn.core.util;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import org.apache.commons.lang3.EnumUtils;
import org.apache.commons.lang3.StringUtils;

import com.zorn.core.constant.Constants.SQL;
import com.zorn.core.constant.Constants.Strings;
import com.zorn.core.constant.Constants.Symbols;

// TODO 支持多种类型 a=1,b=[1,2],c=[3],d=(List){1,2,3,4},e=(List){5}
public final class ParameterParser {

	enum Operator {
		is_null, is_not_null, is_empty, is_not_empty,

		equal, not_equal, less_than, less_than_equal, greater_than, greater_than_equal,

		begin_like, like, end_like, not_begin_like, not_like, not_end_like,

		between, not_between,

		in, not_in;

		public static final Operator getOperator(String operator) {
			return EnumUtils.getEnum(Operator.class, operator.toLowerCase());
		}
	}

	private static final String AND_PREFIX = "and.";
	private static final String OR_PREFIX = "or.";

	public static final Map<String, Object> parse(Map<String, Object> params) {
		params = new HashMap<String, Object>(params);
		List<Map<String, Object>> and = new ArrayList<Map<String, Object>>(), or = new ArrayList<Map<String, Object>>();
		Entry<String, Object> entry;
		String key;
		Object entryValue;
		String[] value;

		for (Iterator<Entry<String, Object>> iterator = params.entrySet().iterator(); iterator.hasNext();) {
			entry = iterator.next();
			key = entry.getKey();
			entryValue = entry.getValue();

			if (entryValue instanceof String[]) {
				value = ((String[]) entry.getValue());

				if (StringUtils.startsWithIgnoreCase(key, AND_PREFIX)) {
					parseParam(and, key, value, false);
					continue;
				} else if (StringUtils.startsWithIgnoreCase(key, OR_PREFIX)) {
					parseParam(or, key, value, true);
					continue;
				} else {
					if (value.length == 1) {
						params.put(key, value[0]);
						continue;
					}
					if (value.length > 1) {
						// FIXME DEBUG
						params.put(key, Arrays.asList(value));
						continue;
					}
				}
			}
		}

		if (and.size() > 0) params.put("and_clause", and);
		if (or.size() > 0) params.put("or_clause", or);

		return params;
	}

	private static final void parseParam(List<Map<String, Object>> container, String name, String[] value, boolean or) {
		String col = getParamColumn(name);

		Operator op = Operator.getOperator(getParamOperator(name));
		/* 默认运算符为等于
		================================================================= */
		if (op == null) op = Operator.equal;

		switch (op) {
		case equal:
			if (or && value.length > 1) batchInstanceParamMap(container, col, Symbols.EQUAL, value);
			else container.add(instanceParamMap(col, Symbols.EQUAL, value[0]));
			break;
		case not_equal:
			batchInstanceParamMap(container, col, SQL.NOT_EQUAL, value);
			break;
		case less_than:
			batchInstanceParamMap(container, col, Symbols.LESS, value);
			break;
		case less_than_equal:
			batchInstanceParamMap(container, col, SQL.LESS_THAN_EQUAL, value);
			break;
		case greater_than:
			batchInstanceParamMap(container, col, Symbols.GREATER, value);
			break;
		case greater_than_equal:
			batchInstanceParamMap(container, col, SQL.GREATER_THAN_EQUAL, value);
			break;

		case begin_like:
			if (or && value.length > 1) batchInstanceParamMap(container, col, SQL.LIKE, value, Strings.EMPTY, Symbols.PERCENT);
			else container.add(instanceParamMap(col, SQL.LIKE, value[0] + Symbols.PERCENT));
			break;
		case like:
			batchInstanceParamMap(container, col, SQL.LIKE, value, Symbols.PERCENT, Symbols.PERCENT);
			break;
		case end_like:
			if (or && value.length > 1) batchInstanceParamMap(container, col, SQL.LIKE, value, Symbols.PERCENT, Strings.EMPTY);
			else container.add(instanceParamMap(col, SQL.LIKE, Symbols.PERCENT + value[0]));
			break;
		case not_begin_like:
			batchInstanceParamMap(container, col, SQL.NOT_LIKE, value, Strings.EMPTY, Symbols.PERCENT);
			break;
		case not_like:
			batchInstanceParamMap(container, col, SQL.NOT_LIKE, value, Symbols.PERCENT, Symbols.PERCENT);
			break;
		case not_end_like:
			batchInstanceParamMap(container, col, SQL.NOT_LIKE, value, Symbols.PERCENT, Strings.EMPTY);
			break;

		case between:
			container.add(instanceParamMap(col, SQL.BETWEEN, value[0], value[1]));
			break;
		case not_between:
			container.add(instanceParamMap(col, SQL.NOT_BETWEEN, value[0], value[1]));
			break;

		case in:
			container.add(instanceParamMap(col, SQL.IN, value));
			break;
		case not_in:
			container.add(instanceParamMap(col, SQL.NOT_IN, value));
			break;

		case is_null:
			container.add(instanceParamMap(col, SQL.IS_NULL));
			break;
		case is_not_null:
			container.add(instanceParamMap(col, SQL.IS_NOT_NULL));
		case is_empty:
			container.add(instanceParamMap(col, Symbols.EQUAL, Strings.EMPTY));
			break;
		case is_not_empty:
			container.add(instanceParamMap(col, SQL.NOT_EQUAL, Strings.EMPTY));
			break;

		default:
			container.add(instanceParamMap(col, Symbols.EQUAL, value));
			break;
		}
	}

	/* Assist
	================================================================= */
	private static final String getParamColumn(String name) {
		if (StringUtils.startsWithIgnoreCase(name, AND_PREFIX)) name = name.replaceFirst(AND_PREFIX, Strings.EMPTY);
		if (StringUtils.startsWithIgnoreCase(name, OR_PREFIX)) name = name.replaceFirst(OR_PREFIX, Strings.EMPTY);

		Operator op = EnumUtils.getEnum(Operator.class, StringUtils.substring(name, StringUtils.lastIndexOf(name, Symbols.DOT) + 1));

		if (op == null) return name;
		else return name.replace(Symbols.DOT + op.toString(), Strings.EMPTY);
	}

	private static final String getParamOperator(String name) {
		String[] split = StringUtils.split(name, Symbols.DOT);
		return split[split.length - 1].toLowerCase();
	}

	/* Instance parameter map.
	================================================================= */
	private static final Map<String, Object> instanceParamMap(Object column, Object operator) {
		Map<String, Object> param = new HashMap<String, Object>(3);
		param.put(Strings.ASSERT, true);
		param.put(Strings.COLUMN, column);
		param.put(Strings.OPERATOR, operator);
		return param;
	}

	private static final Map<String, Object> instanceParamMap(Object column, Object operator, Object value) {
		Map<String, Object> param = new HashMap<String, Object>(4);
		param.put(Strings.SINGLE, true);
		param.put(Strings.COLUMN, column);
		param.put(Strings.OPERATOR, operator);
		param.put(Strings.VALUE, value);
		return param;
	}

	private static final Map<String, Object> instanceParamMap(Object column, Object operator, Object begin, Object end) {
		Map<String, Object> param = new HashMap<String, Object>(5);
		param.put(Strings.BETWEEN, true);
		param.put(Strings.COLUMN, column);
		param.put(Strings.OPERATOR, operator);
		param.put(Strings.BEGIN, begin);
		param.put(Strings.END, end);
		return param;
	}

	private static final Map<String, Object> instanceParamMap(Object column, Object operator, String[] list) {
		Map<String, Object> param = new HashMap<String, Object>(4);
		param.put(Strings.LIST, true);
		param.put(Strings.COLUMN, column);
		param.put(Strings.OPERATOR, operator);
		// ## FIXME DEBUG ##
		param.put(Strings.VALUE, Arrays.asList(list));
		// param.put(Strings.VALUE, list);
		return param;
	}

	/* Batch instance parameter map.
	================================================================= */
	private static final void batchInstanceParamMap(List<Map<String, Object>> container, String column, Object operator, String[] value) {
		for (int i = 0; i < value.length; i++) {
			container.add(instanceParamMap(column, operator, value[i]));
		}
	}

	private static final void batchInstanceParamMap(List<Map<String, Object>> container, String column, Object operator, String[] value, String prefix, Character suffix) {
		batchInstanceParamMap(container, column, operator, value, prefix, suffix.toString());
	}

	private static final void batchInstanceParamMap(List<Map<String, Object>> container, String column, String operator, String[] value, Character prefix, String suffix) {
		batchInstanceParamMap(container, column, operator, value, prefix.toString(), suffix);
	}

	private static final void batchInstanceParamMap(List<Map<String, Object>> container, String column, Object operator, String[] value, Character prefix, Character suffix) {
		batchInstanceParamMap(container, column, operator, value, prefix.toString(), suffix.toString());
	}

	private static final void batchInstanceParamMap(List<Map<String, Object>> container, String column, Object operator, String[] value, String prefix, String suffix) {
		for (int i = 0; i < value.length; i++) {
			container.add(instanceParamMap(column, operator, prefix + value[i] + suffix));
		}
	}

}
